<?php

class FlickrApi {
	
	private $_api_key;
	private $_secret;
	private $_token;
	private $_cache_method;
	
	const CACHE_MYSQL = 'mysql';
	const CACHE_FBDS = 'fbds';
	const CACHE_FILE = 'file';
	
	public $rest_url = 'http://api.flickr.com/services/rest/';
	public $upload_url = 'http://api.flickr.com/services/upload/';
	public $replace_url = 'http://api.flickr.com/services/replace/';
	
	public $photo_sizes = array(
		'square' => '_s',
		'thumbnail' => '_t',
		'small' => '_m',
		'medium' => '',
		'large' => '_b',
		'original' => '_o'
	);
	
	function __construct($api_key, $secret) {
		$this->_api_key = $api_key;
		$this->_secret = $secret;
		$this->_cache_method = self::CACHE_FILE;
	}
	
	static function buildPhotoURL($photo, $size = 'medium') {
		
		// Make sure the size specified exists
		if (!array_key_exists(strtolower($size), $this->photo_sizes)) {
			$size = 'medium';
		}
		
		// If a photoset is passed in, make sure we are using the right ID
		if ($photo['primary']) {
			$photo['id'] = $photo['primary'];
		}
		
		// Build the url
		$url = 'http://farm'.$photo['farm'].'.static.flickr.com/'.$photo['server'].'/'.$photo['id'].'_';
		if ($size = 'original') {
			$url .= $photo['originalsecret'].'_o.'.$photo['originalformat'];
		}
		else {
			$url .= $photo['secret'].$this->photo_sizes[$size].'.jpg';
		}
		return $url;
		
	}
	
	private function cache($params, $response) {
		
		$hash = md5(serialize($params));
		
		// Cache to a MySQL database
		if ($this->_cache_method = self::CACHE_MYSQL) {
			return true;
		}
		// Cache to the Facebook Data Store
		else if ($this->_cache_method = self::CACHE_FBDS) {
			return true;
		}
		// Cache to a file
		else if ($this->_cache_method = self::CACHE_FILE) {
			$file = $this->_cache_dir.'/'.$hash.'.cache';
			$file_stream = fopen($file, 'w');
			$result = fwrite($file_stream, $response);
			fclose($file_stream);
			return $result;
		}
		return false;
		
	}
	
	public function call($method, $params, $cache = true) {
		return $this->request($method, $params, $cache);
	}
	
	private function getCached($params) {
		
		$hash = md5(serialize($params));
		
		// Cache to a MySQL database
		if ($this->_cache_method = self::CACHE_MYSQL) {
			return true;
		}
		// Cache to the Facebook Data Store
		else if ($this->_cache_method = self::CACHE_FBDS) {
			return true;
		}
		// Retrieve from a file
		else if ($this->_cache_method = self::CACHE_FILE) {
			$file = $this->_cache_dir.'/'.$hash.'.cache';
			if (file_exists($file)) {
				return file_get_contents($file);
			}
		}
		return false;
		
	}
	
	private function request($method, $params = array(), $cache = true) {
		
		// Make sure the method has flickr. on the beginning
		if (substr($method, 0, 7) != 'flickr.') {
			$method = 'flickr.'.$method;
		}
		
		// Create our params
		$params = array_merge(array('format' => 'php_serial', 'api_key' => $this->_api_key, 'method' => $method), $params);
		if (!empty($this->_token)) {
			$params = array_merge(array('token' => $this->_token), $params);
		}
		ksort($params);
		
		// Build the proper params
		$encoded_params = array();
		foreach ($params as $key => $value) {
			$encoded_params[] = urlencode($key).'='.urlencode($value);
		}
		
		// Call the API and decode
		$url = $this->rest_url.'?'.implode('&', $encoded_params);
		$response = file_get_contents($url);
		//$this->cache($params, $response);
		$response_obj = unserialize($response);
		
		// Make sure the response is OK
		if ($response_obj['stat'] == 'fail') {
			throw new FlickrApiException($response_obj['message'], $response_obj['code']);
		}
		
		return $response_obj;
		
	}
	
	public function setToken($token) {
		$this->_token = $token;
	}
	
	
	
	/*
	Photos
	*/
	
	/**
	 * Fetch a list of recent public photos from a user's contacts.
	 * http://www.flickr.com/services/api/flickr.photos/getContactsPublicPhotos.html
	 */
	public function photos_getContactsPublicPhotos($user_id, $count = null, $just_friends = null, $single_photo = null, $include_self = null, $extras = null) {
		return $this->request('photos.getContactsPublicPhotos', array('user_id' => $user_id, 'count' => $count, 'just_friends' => $just_friends, 'single_photo' => $single_photo, 'include_self' => $include_self, 'extras' => $extras));
	}
	
	
	/*
	Test
	*/
	
	/**
	 * A testing method which echoes all parameters back in the response.
	 * http://www.flickr.com/services/api/flickr.test.echo.html
	 */
	public function test_echo($params = array()) {
		return $this->request('test.echo', $params);
	}
	
	/**
	 * A testing method which checks if the caller is logged in then returns their username.
	 * http://www.flickr.com/services/api/flickr.test.login.html
	 */
	public function test_login() {
		return $this->request('test.login');
	}
	
	/**
	 * Null test.
	 * http://www.flickr.com/services/api/flickr.test.null.html
	 */
	public function test_null() {
		return $this->request('test.null');
	}
	
}

class FlickrApiException extends Exception {
	
	// Common Flickr API error codes
	const INVALID_SIGNATURE = 96;
	const MISSING_SIGNATURE = 97;
	const LOGIN_FAILED = 98;
	const INSUFFICIENT_PERMISSIONS = 99;
	const INVALID_API_KEY = 100;
	const SERVICE_UNAVAILABLE = 105;
	const FORMAT_NOT_FOUND = 111;
	const METHOD_NOT_FOUND = 112;
	const INVALID_SOAP_ENV = 114;
	const INVALID_XMLRPC_METHOD_CALL = 115;
	
	public static $error_descriptions = array(
		self::INVALID_SIGNATURE => 'The passed signature was invalid.',
		self::MISSING_SIGNATURE => 'THe call required signing but no signature was sent.',
		self::LOGIN_FAILED => 'The login details or auth token passed were invalid.',
		self::INSUFFICIENT_PERMISSIONS => 'The method requires user authentication but the user was not logged in, or the authenticated method call did not have the required permissions',
		self::INVALID_API_KEY => 'The API key passed was not valid or has expired.',
		self::SERVICE_UNAVAILABLE => 'The requested service is temporarily unavailable.',
		self::FORMAT_NOT_FOUND => 'The requested response format was not found.',
		self::METHOD_NOT_FOUND => 'The requested method was not found.',
		self::INVALID_SOAP_ENV => 'The SOAP envelope send in the request could not be parsed.',
		self::INVALID_XMLRPC_METHOD_CALL => 'The XML-RPC request document could not be parsed.',
	);
	
	public function __toString() {
		return 'The Flickr API returned an error: #'.$this->code.' - '.$this->message;
	}
	
}

?>